﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using Mobile.Device.Data;
using System.Collections;
using System.Text.RegularExpressions;

namespace Mobile.Wurfl.BrowserUpdater
{
    public class AppBrowserXmlConverter : XmlShimConverter
    {

        List<Device.Data.Device> _devices;

        public override void WriteStart(XmlWriter xmlWriter, DateTime wurflUpdateTime, List<Device.Data.Device> devices)
        {
            var w = xmlWriter as XmlTextWriter;
            _devices = devices;
            w.WriteStartDocument();
            w.WriteStartElement("browsers");
            w.WriteComment(" ************************************************************************* ");
            w.WriteComment(string.Format(" **** WURFL DATA LAST UPDATED HERE at {0} ", DateTime.Now.ToString("s")));
            w.WriteComment(string.Format(" **** LAST UPDATED ON WURFL SITE {0}      ", wurflUpdateTime.ToString("s")));
            w.WriteComment(" ****  Updated by WURFL Updater - seee http://www.codeplex.com");
            w.WriteComment(" ************************************************************************* ");
        }

        /// <summary>
        ///     <identification>
        ///     <userAgent match="R380 (?'browserMajorVersion'\w*)(?'browserMinorVersion'\.\w*) WAP1\.1" />
        ///     </identification>
        ///     <capture>
        ///     </capture>
        ///     <capabilities>
        ///     <capability name="browser"                     value="Ericsson" />
        ///     <capability name="canInitiateVoiceCall"        value="true" />
        /// </summary>
        /// <param name="device"></param>
        /// <param name="xmlWriter"></param>
        public override void WriteDevice(Mobile.Device.Data.Device device, XmlWriter xmlWriter, List<WurflAspMapping> nameMappings)
        {
            var w = xmlWriter as XmlTextWriter;

            SetDeviceUserAgentMapping(device);


            string parentId = "Default";
            if (device.FallBack != null && device.FallBack != "root")
            {
                parentId = CleanIdentifier(device.FallBack);
            }

            w.WriteStartElement("browser");
            w.WriteAttributeString("id", CleanIdentifier(device.Id));
            w.WriteAttributeString("parentID", parentId);

            w.WriteStartElement("identification");
            w.WriteStartElement("userAgent");

            if (string.IsNullOrEmpty(device.UserAgent))
            {
                device.UserAgent = "( *)";
            }
            else
            {
                device.UserAgent = CleanRegEx(device.UserAgent);
            }

            if (device.UseRegExMatch)
            {
                w.WriteAttributeString("match", device.UserAgent);
            }
            else
            {
                w.WriteAttributeString("nonMatch", device.UserAgent);
            }

            w.WriteEndElement();
            w.WriteEndElement();
            w.WriteElementString("capture", "");
            //w.WriteWhitespace("");
            w.WriteStartElement("capabilities");

            ///     <capability name="browser"                     value="Ericsson" />
            ///     <capability name="canInitiateVoiceCall"        value="true" />
            foreach (var group in device.Groups)
            {
                w.WriteComment(string.Format("Capability Group {0}", group.Id));
                foreach (var capability in group.Capabilities)
                {
                    //-- This maps (and converts) the WURFL capability to the ASP.NET Capabiltiy\
                    //   for Request.Browser.XXX retrieval. We still write the original WURFL capability
                    //   to capability headers so that developers can retrieve based on wurful cap name also...
                    //   these are server-side headers so there is no real client overhead.
                    var aspCap = ConvertCapability(capability, nameMappings);
                    if (aspCap != null)
                    {
                        w.WriteComment(string.Format("** ASP Capability mapping wurfl={0}, ASP={1}", capability.Name, aspCap.Name));
                        w.WriteStartElement("capability");
                        w.WriteAttributeString("name", aspCap.Name);
                        w.WriteAttributeString("value", aspCap.Value);
                        w.WriteEndElement();
                    }

                    w.WriteStartElement("capability");
                    w.WriteAttributeString("name", capability.Name);
                    w.WriteAttributeString("value", capability.Value);
                    w.WriteEndElement();
                }//foreach capability
            }//foreach group

            w.WriteEndElement();
            w.WriteEndElement();

        }

        private void SetDeviceUserAgentMapping(Device.Data.Device device)
        {
            Device.Data.Device d = null;
            string donotmatchkey = "DO_NOT_MATCH_";
            if (!device.UserAgent.StartsWith(donotmatchkey))
            {
                return;
            }

            string keyNotToMatch = device.UserAgent.Replace(donotmatchkey, "").ToLower();
            //-- search in current heirarchy first... save time
            d = SearchUpWards(device.FallBackDevice, keyNotToMatch);

            if (d == null)
            {
                d = SearchFull(keyNotToMatch);
            }

            //-- no match for that key
            if (d == null)
            {
                return;
            }

            //-- possible loop
            if (d.UserAgent.StartsWith(donotmatchkey))
            {
                return;
            }


            device.UserAgent = d.UserAgent;
            device.UseRegExMatch = false;

        }

        private Device.Data.Device SearchFull(string keyNotToMatch)
        {
            return _devices.FirstOrDefault(e => e.Id == keyNotToMatch);
        }

        private Device.Data.Device SearchUpWards(Device.Data.Device device, string keyNotToMatch)
        {
            if (device.Id == keyNotToMatch)
                return device;

            if (device.FallBackDevice == null)
                return null;

            return SearchUpWards(device.FallBackDevice, keyNotToMatch);
         }

        private string CleanIdentifier(string id)
        {
            if (id.Length > 0)
            {
                int i = -1;
                string istring = id[0].ToString();
                if (int.TryParse(istring, out i))
                {
                    id = "N" + id;
                }
            }

            id = id.Replace(" ", "_");

            return id;
        }

        private string CleanRegEx(string ua)
        {
            string s = Regex.Escape(ua);
            s = s.Replace("\\ ", "( *)");

            //test it
            try
            {
                Regex r = new Regex(s);
                return s;
            }
            catch (Exception)
            {
            }

            //-- if that doesn't work ... try escaped

            try
            {
                s = Regex.Escape(ua);
                Regex r = new Regex(s);
                return s;
            }
            catch (Exception)
            {
            }


            //-- if that doesn't work ... try escaped

            try
            {
                Regex r = new Regex(ua);
                return ua;
            }
            catch (Exception)
            {
            }

            return "(INVALID REGEX)";

        }

        public override void WriteEnd(XmlWriter xmlWriter)
        {
            var w = xmlWriter as XmlTextWriter;
            w.WriteEndElement();
            w.WriteEndDocument();
            w.Flush();
        }



        public override AspCapability ConvertCapability(Capability capability, List<WurflAspMapping> nameMappings)
        {
            AspCapability aspCap = new AspCapability();

            var map = (from m in nameMappings
                       where m.WurflName == capability.Name
                       select m).FirstOrDefault();

            if (map == null)
                return null;

            //todo: type conversion
            aspCap.Name = map.AspName;
            aspCap.Value = capability.Value;

            return aspCap;

        }



    }
}
